import { _decorator,  v2 } from "cc";
import { Entity, IEntity } from "./Entity";
import { AnimatorParam } from "../enums/AnimatorParam";
import { DirectionNames } from "../enums/Direction";
import { EventType } from "../enums/EventType";
import { DataManager } from "../manager/DataManager";
import { eventDispatcher } from "../Constants";
const { ccclass } = _decorator;

enum PlayerAction {
    TOP,
    LEFT,
    BOTTOM,
    RIGHT,
    TURNLEFT,
    TURNRIGHT
}

export interface IPlayer extends IEntity {
    targetX: number,
    targetY: number,
}

@ccclass("Player")
export class Player extends Entity {

    private static readonly DIRECTION_DELTA = [v2(0, -1), v2(-1, 0), v2(0, 1), v2(1, 0)];
    private static readonly SPEED = 0.1;

    private _targetX: number = 0;
    private _targetY: number = 0;
    private _isMoving: boolean = false;

    public get targetX(): number {
        return this._targetX;
    }

    public get targetY(): number {
        return this._targetY;
    }

    public getData(): IEntity {
        return {
            x: this.x,
            y: this.y,
            state: Entity.State.IDLE,
            direction: this._direction,
            type: this._type,
            id: this.id
        }
    }

    public setData(value: IEntity): void {
        this._targetX = value.x;
        this._targetY = value.y;
        super.setData(value);
        this._animationController.setValue(AnimatorParam.STEP, true);
    }

    public init(params: IEntity): void {
        this._targetX = params.x;
        this._targetY = params.y;
        super.init(params);
    }

    protected registerEvents(): void {
        eventDispatcher.on(EventType.PLAYER_ACTION, this.handleInput, this);
        eventDispatcher.on(EventType.ATTACK_PLAYER, this.onAttack, this);
    }

    update(): void {
        if (this.x !== this._targetX || this.y !== this._targetY) {
            this.updateXY();
            this.updatePosition();
        }
    }

    private updateXY(): void {
        if (this._targetX < this.x) {
            this.x -= Player.SPEED;
        } else if (this._targetX > this.x) {
            this.x += Player.SPEED;
        }
        if (this._targetY < this.y) {
            this.y -= Player.SPEED;
        } else if (this._targetY > this.y) {
            this.y += Player.SPEED;
        }

        if (Math.abs(this.x - this._targetX) < Player.SPEED && Math.abs(this.y - this._targetY) < Player.SPEED && this._isMoving) {
            this.x = this._targetX;
            this.y = this._targetY;
            this._isMoving = false;


            let doorId = this.checkComplete();
            if(doorId >= 0) {
                eventDispatcher.emit(EventType.COMPLETE, doorId);
            }
        }
    }

    private onAttack(type: number = 0): void {
        this.state = type === 0 ? Entity.State.DEATH: Entity.State.AIRDEATH;
        // this._animationController.setValue(AnimatorParam.STATE, type === 0 ? Entity.State.DEATH : Entity.State.AIRDEATH);
    }

    private handleInput(command: PlayerAction): void {
        if (this._isMoving || this.state !== Entity.State.IDLE) {
            return;
        }

        const enemyId = this.checkCanAttak(command);
        if (enemyId >= 0) {
            this.state = Entity.State.ATTACK;
            // this._animationController.setValue(AnimatorParam.STATE, Entity.State.ATTACK);
            eventDispatcher.emit(EventType.SAVE_RECORD);
            eventDispatcher.emit(EventType.ATTACK_ENEMY, enemyId);
            return;
        }

        if (this.checkCanMove(command)) {
            eventDispatcher.emit(EventType.SAVE_RECORD);

            this.scheduleOnce(()=>{
                this.move(command);

            });

        } else {
            this._animationController.setValue(AnimatorParam.DIRECTION, this.direction);
            this._animationController.setValue(AnimatorParam.ACTION, command);

            this.state = Entity.State.BLOCK;

            // this._animationController.setValue(AnimatorParam.STATE, Entity.State.BLOCK);
            eventDispatcher.emit(EventType.SCENE_SHAKE, command % 4);
        }
    }

    private move(command: PlayerAction): void {
        if (command < DirectionNames.length) {
            const lastX = this._targetX, lastY = this._targetY;
            const playerPos = v2(this._targetX, this._targetY);
            playerPos.add(Player.DIRECTION_DELTA[command]);
            this._targetX = playerPos.x;
            this._targetY = playerPos.y;
            this._isMoving = true;

            eventDispatcher.emit(EventType.PLAYER_MOVE_END, this._targetX, this._targetY, lastX, lastY);
            eventDispatcher.emit(EventType.CREATE_SMOKE, lastX, lastY, command);
        } else {
            const delta = command === PlayerAction.TURNLEFT ? 1 : -1;
            // this.direction = (this.direction + delta + 4) % 4;
            this.state = command === PlayerAction.TURNLEFT ?  Entity.State.TURNLEFT : Entity.State.TURNRIGHT;

            // this._animationController.setValue(AnimatorParam.DIRECTION, this.direction);
            // this._animationController.setValue(AnimatorParam.TURN, command === PlayerAction.TURNLEFT ? -1 : 1);

            eventDispatcher.emit(EventType.PLAYER_ROTATE_END, this._targetX, this._targetY);
        }
    }

    private checkCanMove(command: PlayerAction): boolean {
        const playerPos = v2(this._targetX, this._targetY);
        if (command < DirectionNames.length) {
            const direction: number = command;
            playerPos.add(Player.DIRECTION_DELTA[direction]);

            // if(playerPos.x < 0 || playerPos.x >= )


            if(DataManager.instance.bursts.find(burst => burst.state !== Entity.State.DEATH && burst.x === playerPos.x && burst.y === playerPos.y)) {
                return true;
            }

            // 判断门
            const doorPos = direction === this.direction ? playerPos.clone().add(Player.DIRECTION_DELTA[this.direction]) : playerPos;
            if (DataManager.instance.doors.find(door => door.state !== Entity.State.DEATH && door.x === doorPos.x && door.y === doorPos.y)) {
                return false;
            }

        


            // 判断瓦片
            const weaponPos = playerPos.clone().add(Player.DIRECTION_DELTA[this.direction]);
            const playerTile = DataManager.instance.tileInfo[playerPos.x]?.[playerPos.y];
            const weaponTile = DataManager.instance.tileInfo[weaponPos.x]?.[weaponPos.y];
            if (playerTile && playerTile.moveable && (!weaponTile || weaponTile.turnable)) {
                return true;
            }




        } else {
            const summand = command === PlayerAction.TURNLEFT ? 1 : -1;
            const direction = (this.direction + summand + 4) % 4;
            const weaponPos = [
                playerPos.clone().add(Player.DIRECTION_DELTA[this.direction]),
                playerPos.clone().add(Player.DIRECTION_DELTA[direction]),
                playerPos.clone().add(Player.DIRECTION_DELTA[this.direction]).add(Player.DIRECTION_DELTA[direction]),
            ];

            // 需要判断3个tile的turnable
            for (const pos of weaponPos) {
                if(DataManager.instance.enemies.find(enemy => enemy.state !== Entity.State.DEATH && enemy.x === pos.x && enemy.y === pos.y)) {
                        return false;
                }


                if (DataManager.instance.doors.find(door => door.state !== Entity.State.DEATH && door.x === pos.x && door.y === pos.y)) {
                    return false;
                }


                const weaponTile = DataManager.instance.tileInfo[pos.x]?.[pos.y];
                if (weaponTile && !weaponTile.turnable) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    private checkCanAttak(command: PlayerAction): number {
        if (command >= DirectionNames.length) {
            return -1;
        }

        const scaler = command as number === this.direction ? 2 : 1;
        const expectPos = Player.DIRECTION_DELTA[command].clone().multiplyScalar(scaler).add2f(this._targetX, this._targetY);
        const enemy = DataManager.instance.enemies.find(enemy => {
            return enemy.state !== Entity.State.DEATH && expectPos.x === enemy.x && expectPos.y === enemy.y;
        });
        return enemy ? enemy.id : -1;
    }

    private checkComplete(): number {
        const door = DataManager.instance.doors.find(door => door.state === Entity.State.DEATH && door.x === this.x && door.y === this.y);
        return door ? door.id : -1;
    }
}